#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define THREAD_COUNT 6

typedef struct ThreadInfo
{
    pthread_t threadID;
    int index;
    int numberToCountTo;
    int detachYourself;
    int sleepTime;
} ThreadInfo;

pthread_mutex_t g_mtx = PTHREAD_MUTEX_INITIALIZER;

void* threadFunc(void* arg)
{
    ThreadInfo* info = (ThreadInfo*)arg;
    printf("thread %d, counting to %d, detaching %s\n",
           info->index, info->numberToCountTo, info->detachYourself ? "yes" : "no");

    if (info->detachYourself)
    {
        int r = pthread_detach (pthread_self());
        if (r) {
            fprintf(stderr, "couldn't detach thread %d. Err: %d/%s\n",
                    info->index, r, strerror(r));
        }
    }

    pthread_mutex_lock(&g_mtx);
    int i = 0;
    for (i = 0; i < info->numberToCountTo; ++i) {
        printf(" thread %d counting %d\n", info->index, i);
        usleep(info->sleepTime);
    }
    pthread_mutex_unlock(&g_mtx);

    printf ("thread %d done\n", info->index);
    return (NULL);
}


int main (void)
{
    ThreadInfo threads[THREAD_COUNT];
    int result;
    // initialize the ThreadInfos:
    int i = 0;
    for (i = 0; i < THREAD_COUNT; i++)
    {
        threads[i].index = i;
        threads[i].numberToCountTo = (i + 1) * 2;
        threads[i].detachYourself = (i % 2); // detach odd threads
        threads[i].sleepTime = 500000 + 200000 * i;
        // (make subseuqent threads wait longer between counts)
    }

    // create the threads
    for (i = 0; i < THREAD_COUNT; i++)
    {
        result = pthread_create (&threads[i].threadID, NULL, threadFunc, &threads[i]);
        if (result != 0)
        {
            fprintf (stderr,
                     "could not pthread_create thread %d.  Error: %d/%s\n",
                     i, result, strerror(result));
            exit (EXIT_FAILURE);
        }
    }
    // now rendezvous with all the non-detached threads

    for (i = 0; i < THREAD_COUNT; i++)
    {
        void *retVal;
        if (!threads[i].detachYourself)
        {
            result = pthread_join (threads[i].threadID, &retVal);
            if (result != 0)
            {
                fprintf (stderr, "error joining thread %d.  Error: %d/%s\n",
                         i, result, strerror(result));
            }
            printf ("joined with thread %d\n", i);
       }
    }
    return EXIT_SUCCESS;
} // main
